/*
* Copyright (C) Microsoft. All rights reserved.  
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not 
* use this file except in compliance with the License.  You may obtain a copy 
* of the License at http://www.apache.org/licenses/LICENSE-2.0  
* 
* THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 
* WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
* MERCHANTABLITY OR NON-INFRINGEMENT. 
*
* See the Apache License, Version 2.0 for specific language governing 
* permissions and limitations under the License.
*
*/
#include "pch.h"
#include <journal/models/db/photo_table.h>
#include <journal/models/db/media_file_table.h>
#include <journal/models/db/common_table.h>

using namespace std;
using namespace journal::model::db;

photo_table::photo_table()
{
}

std::shared_ptr<journal::model::db::iphoto_table> photo_table::create(std::shared_ptr<s::iengine> dbEngine)
{
    std::shared_ptr<journal::model::db::photo_table> returnValue = make_shared<photo_table>();
    returnValue->_commonTableClient = std::dynamic_pointer_cast<icommon_table_client>(returnValue);
    returnValue->_commonTable = createCommonTable(returnValue->_commonTableClient, dbEngine, L"Photo");
    return returnValue;
}

std::shared_ptr<icommon_table> photo_table::common()
{
    return _commonTable;
}

void photo_table::defineColumns(std::shared_ptr<s::itable> table)
{
    BAJA_LOCK(_mutex);

    _photoIdColumn = s::createColumn<b::uint32>(L"ID", s::column_type::type_int32);
    table->addColumn(_photoIdColumn);
    
    _pageIdColumn = s::createColumn<b::uint32>(L"PageId", s::column_type::type_int32);
    table->addColumn(_pageIdColumn);

    _activeColumn = s::createColumn<b::uint32>(L"Active", s::column_type::type_int32);
    table->addColumn(_activeColumn);

    _mediaFileIdColumn = s::createColumn<b::uint32>(L"MediaFileId", s::column_type::type_int32);
    table->addColumn(_mediaFileIdColumn);

    _scaleColumn = s::createColumn<b::float32>(L"Scale", s::column_type::type_float32);
    table->addColumn(_scaleColumn);

    _angleColumn = s::createColumn<b::float32>(L"Angle", s::column_type::type_float32);
    table->addColumn(_angleColumn);

    _xColumn = s::createColumn<b::float32>(L"X", s::column_type::type_float32);
    table->addColumn(_xColumn);

    _yColumn = s::createColumn<b::float32>(L"Y", s::column_type::type_float32);
    table->addColumn(_yColumn);
}

void photo_table::defineIndexColumns(std::shared_ptr<s::iindex> primaryKeyIndex)
{
    BAJA_LOCK(_mutex);

    // PK index...
    _pageIdIndexColumn = s::createIndexColumnDefinition(_pageIdColumn, true);
    primaryKeyIndex->addIndexColumn(_pageIdIndexColumn);

    _photoIdIndexColumn = s::createIndexColumnDefinition(_photoIdColumn, true);
    primaryKeyIndex->addIndexColumn(_photoIdIndexColumn);

    // active index
    _activeIndexColumn = s::createIndexColumnDefinition(_activeColumn, true);

    _activeIndex = s::createIndex(L"ActivePhoto_Index", false, false);
    _activeIndex->addIndexColumn(_activeIndexColumn);

    _commonTable->tableDefinition()->addIndex(_activeIndex);
}

void photo_table::defineRowCells(std::shared_ptr<std::vector<std::shared_ptr<s::icell>>> row)
{
    BAJA_LOCK(_mutex);

    _photoIdCell = _photoIdColumn->createCell(0);
    _pageIdCell = _pageIdColumn->createCell(0);
    _activeCell = _activeColumn->createCell(1);
    _mediaFileIdCell = _mediaFileIdColumn->createCell(0);
    _scaleCell = _scaleColumn->createCell(0.0f);
    _angleCell = _angleColumn->createCell(0.0f);
    _xCell = _xColumn->createCell(0.0f);
    _yCell = _yColumn->createCell(0.0f);

    row->push_back(_photoIdCell);
    row->push_back(_pageIdCell);
    row->push_back(_activeCell);
    row->push_back(_mediaFileIdCell);
    row->push_back(_scaleCell);
    row->push_back(_angleCell);
    row->push_back(_xCell);
    row->push_back(_yCell);
}

void photo_table::insert(b::uint32 photoId,
                         b::uint32 pageId,
                         bool active,
                         b::uint32 mediaFileId,
                         b::float32 scale,
                         b::float32 angle,
                         b::float32 x,
                         b::float32 y)
{
    BAJA_LOCK(_mutex);

    _photoIdCell->setValue(photoId);
    _pageIdCell->setValue(pageId);
    _activeCell->setValue(active);
    _mediaFileIdCell->setValue(mediaFileId);
    _scaleCell->setValue(scale);
    _angleCell->setValue(angle);
    _xCell->setValue(x);
    _yCell->setValue(y);

    _commonTable->insert(_commonTable->stagingRow());
}

void photo_table::update(b::uint32 photoId,
                  b::uint32 pageId,
                  bool active,
                  b::uint32 mediaFileId,
                  b::float32 scale,
                  b::float32 angle,
                  b::float32 x,
                  b::float32 y)
{
    BAJA_LOCK(_mutex);

    _photoIdCell->setValue(photoId);
    _pageIdCell->setValue(pageId);
    _activeCell->setValue(active ? 1 : 0);
    _mediaFileIdCell->setValue(mediaFileId);
    _scaleCell->setValue(scale);
    _angleCell->setValue(angle);
    _xCell->setValue(x);
    _yCell->setValue(y);

    _commonTable->update(_commonTable->stagingRow(),
                          static_cast<baja::uint32>(_commonTable->primaryIndex()->getIndexColumns()->size()));
}

void photo_table::load(b::uint32 photoId,
            b::uint32 pageId,
            bool& active,
            b::uint32& mediaFileId,
            b::float32& scale,
            b::float32& angle,
            b::float32& x,
            b::float32& y)
{
    BAJA_LOCK(_mutex);

    // select all columns...
    std::shared_ptr<s::iresult> result = s::createResult();
    result->addColumn(_photoIdColumn);
    result->addColumn(_pageIdColumn);
    result->addColumn(_activeColumn);
    result->addColumn(_mediaFileIdColumn);
    result->addColumn(_scaleColumn);
    result->addColumn(_angleColumn);
    result->addColumn(_xColumn);
    result->addColumn(_yColumn);

    // execute
    this->read(photoId, pageId, result);

    // iterator through the result...
    std::shared_ptr<std::vector<std::shared_ptr<s::iresult_row>>> rows = result->getRows();

    BAJA_ASSERT(rows->size() == 1, L"One (and only one) records is expected.");

    std::vector<std::shared_ptr<s::iresult_row>>::iterator rowIt;
    for (rowIt = rows->begin(); rowIt < rows->end(); ++rowIt)
    {
        std::shared_ptr<s::iresult_row> row = *rowIt;
        std::shared_ptr<std::vector<std::shared_ptr<s::icell>>> cells = row->getCells();

        std::vector<std::shared_ptr<s::icell>>::iterator cellIt;
        for (cellIt = cells->begin(); cellIt < cells->end(); ++cellIt)
        {
            std::shared_ptr<s::icell> cell = *cellIt;
            if( cell->getColumnId() == _photoIdColumn->getId() )
            {
                std::shared_ptr<s::icell_typed<b::uint32>> typedCell = std::dynamic_pointer_cast<s::icell_typed<b::uint32>>(cell);
                if (photoId == typedCell->getValue())
                {
                    continue;
                }
                else
                {
                    BAJA_ASSERT(false, L"The query must return the requested row only!");
                }
            }
            if( cell->getColumnId() == _pageIdColumn->getId() )
            {
                // we already know this one...
                std::shared_ptr<s::icell_typed<b::uint32>> typedCell = std::dynamic_pointer_cast<s::icell_typed<b::uint32>>(cell);
                BAJA_ASSERT(pageId == typedCell->getValue(), L"The query must return a single record!");
                continue;
            }
            if( cell->getColumnId() == _activeColumn->getId() )
            {
                std::shared_ptr<s::icell_typed<b::uint32>> typedCell = std::dynamic_pointer_cast<s::icell_typed<b::uint32>>(cell);
                active = typedCell->getValue() == 0 ? false : true;
                continue;
            }
            if( cell->getColumnId() == _mediaFileIdColumn->getId() )
            {
                std::shared_ptr<s::icell_typed<b::uint32>> typedCell = std::dynamic_pointer_cast<s::icell_typed<b::uint32>>(cell);
                mediaFileId = typedCell->getValue();
                continue;
            }
            if( cell->getColumnId() == _scaleColumn->getId() )
            {
                std::shared_ptr<s::icell_typed<b::float32>> typedCell = std::dynamic_pointer_cast<s::icell_typed<b::float32>>(cell);
                scale = typedCell->getValue();
                continue;
            }
            if( cell->getColumnId() == _angleColumn->getId() )
            {
                std::shared_ptr<s::icell_typed<b::float32>> typedCell = std::dynamic_pointer_cast<s::icell_typed<b::float32>>(cell);
                angle = typedCell->getValue();
                continue;
            }
            if( cell->getColumnId() == _xColumn->getId() )
            {
                std::shared_ptr<s::icell_typed<b::float32>> typedCell = std::dynamic_pointer_cast<s::icell_typed<b::float32>>(cell);
                x = typedCell->getValue();
                continue;
            }
            if( cell->getColumnId() == _yColumn->getId() )
            {
                std::shared_ptr<s::icell_typed<b::float32>> typedCell = std::dynamic_pointer_cast<s::icell_typed<b::float32>>(cell);
                y = typedCell->getValue();
                continue;
            }
        }
    }
}

void photo_table::read(b::uint32 photoId, 
                       b::uint32 pageId,
                       std::shared_ptr<s::iresult> result)
{
    BAJA_LOCK(_mutex);

    _photoIdCell->setValue(photoId);
    _pageIdCell->setValue(pageId);

    _commonTable->read(_commonTable->stagingRow(),
                        static_cast<baja::uint32>(_commonTable->primaryIndex()->getIndexColumns()->size()),
                       result);
}

void photo_table::readActiveIds(
    b::uint32 pageId,
    std::vector<b::uint32>& ids)
{
    BAJA_LOCK(_mutex);

    _pageIdCell->setValue(pageId);

    _commonTable->readActiveIds(
        _commonTable->stagingRow(),
        _photoIdColumn,
        _activeColumn,
        ids);
}

void photo_table::dispose(
    b::uint32 photoId,
    b::uint32 pageId)
{
    BAJA_LOCK(_mutex);

    _photoIdCell->setValue(photoId);
    _pageIdCell->setValue(pageId);

    _commonTable->dispose(_commonTable->stagingRow());
}

void photo_table::dispose(
    b::uint32 pageId)
{
    BAJA_LOCK(_mutex);

    _pageIdCell->setValue(pageId);
    _commonTable->dbEngine()->dispose(
        _commonTable->tableDefinition(),
        _commonTable->primaryIndex(),
        _commonTable->stagingRow(),
        1,          // only take into account pageId
        false);
}

void photo_table::disposeInactiveRecords()
{
    BAJA_LOCK(_mutex);

    _activeCell->setValue(0);

    _commonTable->disposeInactiveRecords(
        _activeIndex,
        _commonTable->stagingRow(),
        static_cast<baja::uint32>(_activeIndex->getIndexColumns()->size()),
        true);
}

void photo_table::getMediaFileIds(
    std::set<b::uint32>& mediaFileIds)
{
    BAJA_LOCK(_mutex);

    std::shared_ptr<s::iresult> result = s::createResult();
    result->addColumn(_mediaFileIdColumn);

    // execute
    this->common()->readAll(result);

    // iterator through the result...
    std::shared_ptr<std::vector<std::shared_ptr<s::iresult_row>>> rows = result->getRows();

    for (auto &row : *rows)
    {
        std::shared_ptr<std::vector<std::shared_ptr<s::icell>>> cells = row->getCells();
        for (auto &cell : *cells)
        {
            BAJA_ASSERT( cell->getColumnId() == _mediaFileIdColumn->getId(), L"result contains unexpected columns");

            std::shared_ptr<s::icell_typed<b::uint32>> typedCell = std::dynamic_pointer_cast<s::icell_typed<b::uint32>>(cell);
            mediaFileIds.insert(typedCell->getValue());
        }
    }
}

std::shared_ptr<journal::model::db::iphoto_table> journal::model::db::createPhotoTable(std::shared_ptr<s::iengine> dbEngine)
{
    return photo_table::create(dbEngine);
}
